package com.zhenhunfan.io.model.nio.demo01.demo01;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;

/**
 * <p>Title: Handler</p>
 * <p>Description: </p>
 *
 * @author zhenhunfan
 * @version 1.0.0
 * @date 2021/4/1
 */
public class Handler implements Runnable{

    private SocketChannel socketChannel;
    private SelectionKey selectionKey;

    private ByteBuffer readBuffer = ByteBuffer.allocate(1024);
    private ByteBuffer sendBuffer = ByteBuffer.allocate(2048);

    private final static int READ = 0;//读取就绪
    private final static int SEND = 1;//响应就绪

    private int status = READ;

    public Handler(SocketChannel socketChannel, Selector selector) throws IOException {
        this.socketChannel = socketChannel;//接收客户端连接
        this.socketChannel.configureBlocking(false);//设置非阻塞模式（selector仅允非阻塞模式）

        selectionKey = socketChannel.register(selector, SelectionKey.OP_READ);//将该客户端注册
        selectionKey.attach(this);//附加处理对象，当前是Handler对象，run是对象处理业务的方法
    }

    public void run() {
        try {
            switch (status){
                case READ:
                    read();
                    break;
                case SEND:
                    send();
                    break;
                default:
            }
        }catch (IOException e){//这里的异常处理是做了汇总，常出的异常就是server端还有未读/未写完的数据
            System.err.println("read或send时发生异常！异常信息："+e.getMessage());
            selectionKey.cancel();

            try {
                socketChannel.close();
            } catch (IOException ex) {
                System.out.println("关闭通道时发生异常！异常信息："+ex.getMessage());
                ex.printStackTrace();
            }
        }
    }

    private void read() throws IOException {
        if(selectionKey.isValid()){
            readBuffer.clear();
            int count = socketChannel.read(readBuffer);//read方法结束，意味着本次“读就绪”变为“读完毕”
            if(count>0){
                System.out.println(String.format("收到来自 %s 的消息：%s",
                        socketChannel.getRemoteAddress(),new String(readBuffer.array())));
                status = SEND;
                selectionKey.interestOps(SelectionKey.OP_WRITE);//设置感兴趣的事件为写事件
            }else{
                //读模式下拿到的值为-1，说明客户端已经断开连接，那么将对应的selectKey从selector里清除
                //所以在这种场景下，（服务器程序）你需要关闭socketChannel并且取消key
                selectionKey.cancel();
                socketChannel.close();
                System.out.println("read时--------连接关闭");
            }
        }
    }

    private void send() throws IOException {
        if(selectionKey.isValid()){
            sendBuffer.clear();
            sendBuffer.put("response ok".getBytes());
            sendBuffer.flip();
            int count = socketChannel.write(sendBuffer);//write方法结束
            //意味着本次写就绪变成了写完毕，标记着一次事件的结束
            if(count<0){
                //同上，write场景下，取到-1，也意味着客户端断开连接
                selectionKey.cancel();
                socketChannel.close();
                System.out.println("send时--------连接关闭");
            }
            //没断开连接，则再次切换到读
            status =  READ;
            selectionKey.interestOps(SelectionKey.OP_READ);//设置感兴趣的事件为读事件
        }
    }
}
